package reinas;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class AlgoGen {
    public Integer conMuta= 0;
    public Integer N = 4;//Número de individuos (tamaño de la población)
    public Integer G= 50;//Número de generaciones
    public Double Pc= .6; //Probabilidad de cruza (0.5 a 0.9) (Por Pareja)
    public Double Pm= 0.1; //Probabilidad de mutación (0.05 a 0.2) (Por Hijo Individual)
    public Double Pe= 0.2;//Porcentaje de elitismo (% de individuos que pasan por la seleccion elitista)
    List<Individuo> poblacion;
    List<Individuo> poblacionSeleccion;
    List<Individuo> poblacionTemp;
    int elitismo=10;
    int solucion=-1;
    public void run(){
        boolean condCierre=false;
        
        //1.- se crea una población (cada individuo es una posible solución)
        poblacion = new ArrayList();
        
        for(int i=0;i<N;i++){
            poblacion.add(new Individuo());
        }
        //2.-Calificar con la función aptitud a cada individuo
        //2.a.-Ordenarlos por su evaluación
        aptitud();
        solucion = buscasolucion();
        condCierre = solucion<0?false:true;
        int g=0;
        while(!condCierre){
            //System.out.println("padres");
            //System.out.print(poblacion);
            elitismo();
            seleccion();
            //System.out.println("seleccionados");
            //System.out.println(poblacionSeleccion);
            cruza();
            //System.out.println("Cruzados");
            //System.out.println(poblacion);
            aptitud();
            solucion = buscasolucion();
            if(solucion>=0)
                condCierre=true;
            else if(g>=G)
                condCierre=true;
            g++;
        }
        //Imprimiendo el primer elemento de la población
        if(solucion>=0){
            System.out.println("La solucion en la generación "+g+" es:\n"+poblacion.get(solucion).ImpTablero());
        }else{
            int minColis=0;
            System.out.println("Se excede el número de "+G+" generaciones\n");
            for(int i=0;i<poblacion.size();i++){
            if(poblacion.get(i).colisiones<poblacion.get(minColis).colisiones)
                minColis=i;
            }
            System.out.println("La solución más cercana es:\n"+poblacion.get(minColis).ImpTablero());
        }
    }
    public void aptitud(){
        for(Individuo individuo : poblacion){
            Integer colisiones = 0;
            for(int i=0;i<=individuo.cromosoma.length;i++){
                for(int j=i+1;j<individuo.cromosoma.length;j++){
                    if(Math.abs(i-j)==Math.abs(individuo.cromosoma[i]-individuo.cromosoma[j])){
                        colisiones+=1;
                    }
                    if(individuo.cromosoma[i]==individuo.cromosoma[j])
                        colisiones+=1;
                }
            }   
            individuo.evaluacion = 1-(colisiones/15.0);
            individuo.colisiones=colisiones;
        }
    }
    public int buscasolucion(){
        for(int i=0;i<poblacion.size();i++){
            if(poblacion.get(i).colisiones==0)
                return i;
        }
        return -1;
    }
    public void seleccion() {
        
        poblacionSeleccion= new ArrayList();
        Random r = new Random();
        Individuo individuo = null;
        Individuo individuoAux = null;
        int j = 0;
        int newN=N;
        double sumRebanada = 0.0;
        double pastel = 0.0;
        double elegido = 0.0;
        boolean porcionmeta = false;

        for (int i = 0; i < poblacion.size(); i++) {
            pastel += poblacion.get(i).evaluacion;
        }
        //pastel *= 0.01;
        for (int i = 0; i < poblacion.size(); i++) {
            individuo = poblacion.get(i);
            individuo.ps = ((individuo.evaluacion*100) / pastel);
        }
        int maxRuleta=100;
        for(int i=0;i<newN;i++){
            int aux=maxRuleta<=0?1:maxRuleta;
        elegido = r.nextInt(aux);
        sumRebanada=0;j=0;porcionmeta=false;
        while (!porcionmeta) {
            if(j>poblacion.size()-1){
                    maxRuleta-=poblacion.get(poblacion.size()-1).ps;
                    poblacionSeleccion.add(poblacion.remove(poblacion.size()-1));
                    porcionmeta = true;
            }else{
                sumRebanada += poblacion.get(j).ps;
                if (elegido <= sumRebanada){
                        maxRuleta-=poblacion.get(j).ps;
                        poblacionSeleccion.add(poblacion.remove(j));
                        porcionmeta = true;
                } else {
                    j++;
                }
            }
        }
       //newN--;
      }
    }//Fin Seleccion
    public void elitismo(){
        poblacionTemp= new ArrayList();
        elitismo=(int)(Pe*N/100);
        if(elitismo!=0){
            for(int k=0;k<elitismo;k++)
                poblacionTemp.add(poblacion.remove(k));
                N--;
            } 
   }
    public void cruza() {
        Individuo papa, mama ;
        Random r = new Random();
        for (int i = 0; i < N / 2; i++) {
            Individuo hijo1 = new Individuo(), hijo2 = new Individuo();
            if (r.nextInt(1001) <= Pc * 1000){
                int ptoCorte = r.nextInt(5) + 1;
                papa = poblacionSeleccion.remove(0);
                mama = poblacionSeleccion.remove(0);
                for (int j = 0; j < ptoCorte; j++) {
                    hijo1.cromosoma[j] = papa.cromosoma[j];
                    hijo2.cromosoma[j] = mama.cromosoma[j];
                }
                boolean bandera;
                int aux = ptoCorte, j;
                for (j = 0; j < mama.cromosoma.length; j++) {
                    bandera = true;
                    for (int k = 0; k < ptoCorte; k++) {
                        if (mama.cromosoma[j].equals(hijo1.cromosoma[k])) {
                            bandera = false;
                            break;
                        }
                    }
                    if (bandera) {
                        hijo1.cromosoma[aux] = mama.cromosoma[j];
                        aux += 1;
                        if (aux >= mama.cromosoma.length) {
                            break;
                        }
                    }
                }
                aux = ptoCorte;
                for (j = 0; j < papa.cromosoma.length; j++) {
                    bandera = true;
                    for (int k = 0; k < ptoCorte; k++) {
                        if (papa.cromosoma[j] == hijo2.cromosoma[k]) {
                            bandera = false;
                            break;
                        }
                    }
                    if (bandera) {
                        hijo2.cromosoma[aux] = papa.cromosoma[j];
                        aux += 1;
                        if (aux >= papa.cromosoma.length) {
                            break;
                        }
                    }
                }
                poblacion.add(hijo1);
                poblacion.add(hijo2);
                Random PosMuta = new Random();
                if (PosMuta.nextInt(1001) <= Pm * 1000) {
                    //System.out.println("muto");
                    int n = PosMuta.nextInt(2);
                    if (n == 0) {
                        poblacion.set(poblacion.size() - 1, mutacion((poblacion.get(poblacion.size() - 1))));
                    } else if (n == 1) {
                        poblacion.set(poblacion.size() - 2, mutacion((poblacion.get(poblacion.size() - 2))));
                    }
                }
            }else{
                poblacion.add(poblacionSeleccion.remove(0));
                poblacion.add(poblacionSeleccion.remove(0));
            }
        }
        poblacion.addAll(poblacionTemp);
        N+=poblacionTemp.size();
        poblacionTemp=null;
    }
    
    public Individuo mutacion(Individuo pobIndividuo){
        Random ran =new Random();        
        //System.out.println("\n"+pobIndividuo);
        int aX=ran.nextInt(pobIndividuo.cromosoma.length);
        int aY=ran.nextInt(pobIndividuo.cromosoma.length);
        while(pobIndividuo.cromosoma[aX]==aY){//Muta el valor de alguna reina
            aY=ran.nextInt(pobIndividuo.cromosoma.length);
        }
        pobIndividuo.cromosoma[aX]=aY;
        //System.out.println("\n"+pobIndividuo);
        conMuta++;
        return pobIndividuo;
    }
}
